package org.dark.fund.service;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.dark.fund.api.IFundService;
import org.dark.fund.api.IRateReturnService;
import org.dark.fund.api.dto.FundBaseInfoToday;
import org.dark.fund.api.dto.FundRateRecently;
import org.dark.fund.dao.entity.Archives;
import org.dark.fund.dao.entity.Fund;
import org.dark.fund.dao.entity.RateReturn;
import org.dark.fund.dao.mapper.FundMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author wanlh
 * @since 2021-04-29
 */
@Slf4j
@Service
public class FundServiceImpl extends ServiceImpl<FundMapper, Fund> implements IFundService {

    @Autowired
    IRateReturnService rateReturnService;

    @Override
    public List<Fund> getBatchFund(List<String> fundIds) {
        return lambdaQuery().in(Fund::getFundCode, fundIds).list();
    }

    @Async
    @Override
    public Future<Set<Fund>> queryFundByFundCode(String fundCode) {
        Set<Fund> funds = new HashSet<>(lambdaQuery().like(Fund::getFundCode, fundCode)
                .list());
        return AsyncResult.forValue(funds);
    }

    @Async
    @Override
    public Future<Set<Fund>> queryFundByFundName(String fundName) {
        Set<Fund> funds = new HashSet<>(lambdaQuery().like(Fund::getFundName, fundName)
                .list());
        return AsyncResult.forValue(funds);
    }

    @Async
    @Override
    public Future<Set<Fund>> queryFundByManagerName(String managerName) {
        Set<Fund> funds = new HashSet<>(lambdaQuery().like(Fund::getFundManager, managerName)
                .list());
        return AsyncResult.forValue(funds);
    }

    @Async
    @Override
    public Future<Set<Fund>> queryFundByShortSpell(String shortSpell) {
        Set<Fund> funds = new HashSet<>(lambdaQuery().like(Fund::getFundShortSpell, shortSpell)
                .list());
        return AsyncResult.forValue(funds);
    }

    @Async
    @Override
    public Future<Set<Fund>> queryFundByLongSpell(String longSpell) {
        Set<Fund> funds = new HashSet<>(lambdaQuery().like(Fund::getFundLongSpell, longSpell)
                .list());
        return AsyncResult.forValue(funds);
    }

    @Override
    public Set<Fund> queryFundAvailable(String condition) {
        Future<Set<Fund>> fundCode = queryFundByFundCode(condition);
        Future<Set<Fund>> fundName = queryFundByFundName(condition);
        Future<Set<Fund>> managerName = queryFundByManagerName(condition);
        Future<Set<Fund>> shortSpell = queryFundByShortSpell(condition);
        Future<Set<Fund>> longSpell = queryFundByLongSpell(condition);
        List<Future<Set<Fund>>> futures = new ArrayList<>(5);
        futures.add(fundCode);
        futures.add(fundName);
        futures.add(managerName);
        futures.add(shortSpell);
        futures.add(longSpell);
        Set<Fund> resultSet = new HashSet<>();
        futures.forEach(future -> {
            try {
                resultSet.addAll(future.get());
            } catch (InterruptedException | ExecutionException e) {
                log.error("多线程执行异常/中断", e);
            }
        });
        return resultSet;
    }

    @Override
    public FundBaseInfoToday queryFundBaseInfoToday(String fundCode) {
        RateReturn rateInfo = rateReturnService.getTodayRate(fundCode);
        Fund fund = lambdaQuery().eq(Fund::getFundCode, fundCode).one();
        return FundBaseInfoToday.builder().fundCode(fundCode).fundName(fund.getFundName())
                .fundRiskLevel(fund.getFundRiskLevel())
                .fundManagerName(fund.getFundManager())
                .rate(rateInfo.getLastOneDayRate()).value(new BigDecimal(rateInfo.getNetAssetValue()))
                .build();
    }

    @Override
    public FundRateRecently queryFundRateRecently(String fundCode, int offset) {
        List<RateReturn> rateReturns = rateReturnService.getRateRecently(fundCode, offset);
        rateReturns.sort((r1, r2) -> (int) (DateUtil.parse(r1.getFundDate()).getTime() - DateUtil.parse(r2.getFundDate()).getTime()));
//        Map<String, BigDecimal> rates = rateReturns.stream()
//                .collect(Collectors.toMap(RateReturn::getFundDate, RateReturn::getLastOneDayRate));
//        Map<String, BigDecimal> rates = new HashMap<>(rateReturns.size());
//        rateReturns.forEach(rateReturn -> {
//            rates.put(rateReturn.getFundDate(), rateReturn.getLastOneDayRate());
//        });
        List<FundRateRecently.DateValuePair> rates = new ArrayList<>(rateReturns.size());
        rateReturns.forEach(rateReturn -> rates.add(FundRateRecently.DateValuePair.of(rateReturn.getFundDate(),rateReturn.getLastOneDayRate())));
        return FundRateRecently.builder().fundCode(fundCode).offset(offset).rates(rates).build();
    }

    @Override
    public Archives getArchives(String fundCode) {
        Archives archives = new Archives();
        // 基金基本信息 规模\成立时间
        Future<Set<Fund>> fundFuture = queryFundByFundCode(fundCode);
        // 基金最大最小年增长率

        RateReturn maxRate = rateReturnService.getMaxYearDown(fundCode);
        BigDecimal max = BigDecimal.ZERO;
        if (null != maxRate) {
            max = maxRate.getLastOneYearRate();
        }
        BigDecimal min = BigDecimal.ZERO;
        RateReturn minRate = rateReturnService.getMaxYearUp(fundCode);
        if (null != minRate) {
            min = minRate.getLastOneYearRate();
        }

        // 增长降低年份占比
        double upRate = rateReturnService.getYearUpRate(fundCode);
        double downRate = 1 - upRate;
        // 年末近值
        archives.setFundCode(fundCode);
        Fund fund = null;
        try {
            fund = fundFuture.get().iterator().next();
        } catch (InterruptedException | ExecutionException e) {
            log.error("多线程执行异常/中断", e);
        }
        if (fund == null) {
            return null;
        }
        archives.setMaxYearUp(max);
        archives.setMaxYearDown(min);
        archives.setYearDownPercent(downRate);
        archives.setYearUpPercent(upRate);
        archives.setFundScale(fund.getFundScale());
        archives.setFundSetupDate(fund.getFundSetupDate());
        return archives;
    }

}
